import inspect
import aiohttp
import asyncio
from pydantic import BaseModel
from typing import Any, Callable, Coroutine

from settings import settings


class BaseApi:
    class Request(BaseModel):
        params = {}

    __slots__ = ['request_queue_uuid', 'connection']

    def __init__(self, request_queue_uuid: str, base_routers=True) -> None:
        self.request_queue_uuid = request_queue_uuid
        self.connection = None

    async def send_rpc_request_message(self, method: str, params: dict | list = None,
                                 handle_result: Callable[[Any], Any] = lambda r: r,
                                 timeout=5*60
                                 ) -> Coroutine[Any, Any, Any | None] | Any | None:

        if self.get_connection() is None:
            return None

        async def request():
            await asyncio.sleep(0.0001)
            try:
                async with aiohttp.request(
                    'POST', f'http://localhost/{method}',
                    connector=self.get_connection(),
                    data=BaseApi.Request(params=params).json(),
                    timeout=aiohttp.client.ClientTimeout(total=timeout)
                ) as resp:
                    if resp.status != 200:
                        return None
                    result = (await resp.json())
                    return handle_result(result['result'])
            except Exception as e:
                #print(f'cen`t call {method} from send_rpc_request_message {self.request_queue_uuid}: {e}')
                return None

        if params is None:
            params = {}

        return await request()

    async def ping(self) -> str | None:
        return await self.send_rpc_request_message(inspect.currentframe().f_code.co_name)

    async def get_pid(self) -> int | None:
        return await self.send_rpc_request_message(inspect.currentframe().f_code.co_name)

    async def get_service_name(self) -> str | None:
        return await self.send_rpc_request_message(inspect.currentframe().f_code.co_name)

    def get_connection(self):
        if self.connection is not None and self.connection.closed:
            self.connection = None

        if self.connection is None:
            try:
                self.connection = aiohttp.UnixConnector(
                    path=f'{settings.ROOT_PATH}/environment/sockets/{self.request_queue_uuid}.sock'
                )
            except Exception as e:
                #print(f'cen`t connect to {self.request_queue_uuid}: {e}')
                self.connection = None

        return self.connection

    def __del__(self):
        if self.connection is not None:
            self.connection.close()
